package com.sky.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sky.config.WeChatPayConfig;
import com.sky.entity.Order;
import com.sky.mapper.OrderMapper;
import com.sky.service.PayService;
import com.sky.vo.PayVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import javax.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.Instant;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
@Slf4j
public class PayServiceImpl implements PayService {

    @Autowired
    private WeChatPayConfig weChatPayConfig;

    @Autowired
    private OrderMapper orderMapper;

    private final ObjectMapper objectMapper = new ObjectMapper();
    private PrivateKey merchantPrivateKey;
    private X509Certificate wechatPayCertificate;
    private String platformCertSerialNo;

    @PostConstruct
    public void init() {
        try {
            // 加载商户私钥（apiclient_key.pem）
            ClassPathResource privateKeyResource = new ClassPathResource(weChatPayConfig.getPrivateKeyPath().replace("classpath:", ""));
            String privateKeyPEM = StreamUtils.copyToString(privateKeyResource.getInputStream(), StandardCharsets.UTF_8)
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            merchantPrivateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyPEM)));

            // 加载商户证书（apiclient_cert.pem），用于获取serial_no
            ClassPathResource mchCertResource = new ClassPathResource(weChatPayConfig.getMerchantCertPath().replace("classpath:", ""));
            CertificateFactory mchCf = CertificateFactory.getInstance("X.509");
            X509Certificate mchCert = (X509Certificate) mchCf.generateCertificate(mchCertResource.getInputStream());
            platformCertSerialNo = mchCert.getSerialNumber().toString(16).toUpperCase();

            // 加载微信支付平台证书（wechatpay_*.pem），用于回调验签
            ClassPathResource platformCertResource = new ClassPathResource(weChatPayConfig.getPlatformCertPath().replace("classpath:", ""));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            wechatPayCertificate = (X509Certificate) cf.generateCertificate(platformCertResource.getInputStream());

            log.info("微信支付V3证书初始化成功，商户证书序列号：{}，平台证书序列号：{}", platformCertSerialNo, wechatPayCertificate.getSerialNumber().toString(16).toUpperCase());
        } catch (Exception e) {
            log.error("初始化微信支付证书失败", e);
            throw new RuntimeException("初始化微信支付证书失败", e);
        }
    }

    /**
     * 微信支付V3统一下单接口
     */
    @Override
    public PayVO payment(String orderNumber, String openid) {
        log.info("开始微信支付V3统一下单，订单号：{}，openid：{}", orderNumber, openid);

        Order order = orderMapper.getByNumber(orderNumber);
        if (order == null) {
            log.error("订单不存在，订单号：{}", orderNumber);
            throw new RuntimeException("订单不存在");
        }

        try {
            // 构建请求体
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("appid", weChatPayConfig.getAppId());
            requestBody.put("mchid", weChatPayConfig.getMchId());
            requestBody.put("description", "签证服务费用");
            requestBody.put("out_trade_no", orderNumber);
            requestBody.put("notify_url", weChatPayConfig.getNotifyUrl());

            // 金额信息（V3使用分为单位）
            Map<String, Object> amount = new HashMap<>();
            amount.put("total", order.getAmount().multiply(new BigDecimal("100")).intValue());
            amount.put("currency", "CNY");
            requestBody.put("amount", amount);

            // 支付者信息
            Map<String, String> payer = new HashMap<>();
            payer.put("openid", openid);
            requestBody.put("payer", payer);

            String jsonBody = objectMapper.writeValueAsString(requestBody);
            String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

            // 发送请求
            String responseBody = sendV3Request("POST", url, jsonBody);
            JsonNode responseJson = objectMapper.readTree(responseBody);

            // 获取prepay_id
            String prepayId = responseJson.get("prepay_id").asText();

            // 构建前端JSAPI调起支付参数
            return buildJSAPIPayVO(prepayId);

        } catch (Exception e) {
            log.error("微信支付V3统一下单失败，订单号：{}", orderNumber, e);
            throw new RuntimeException("微信支付统一下单失败", e);
        }
    }

    /**
     * 处理微信支付V3结果通知
     */
    @Override
    public void paySuccess(String notifyData) {
        log.info("接收到微信支付V3结果通知");
        try {
            JsonNode notifyJson = objectMapper.readTree(notifyData);
            
            // 验证签名
            if (!verifyNotifySignature(notifyData)) {
                log.error("微信支付V3通知签名验证失败");
                throw new RuntimeException("通知签名验证失败");
            }

            // 解密资源数据
            JsonNode resource = notifyJson.get("resource");
            String ciphertext = resource.get("ciphertext").asText();
            String associatedData = resource.get("associated_data").asText();
            String nonce = resource.get("nonce").asText();

            String decryptedData = decryptResource(ciphertext, associatedData, nonce);
            JsonNode paymentData = objectMapper.readTree(decryptedData);

            // 处理支付结果
            String tradeState = paymentData.get("trade_state").asText();
            if ("SUCCESS".equals(tradeState)) {
                String orderNumber = paymentData.get("out_trade_no").asText();
                String transactionId = paymentData.get("transaction_id").asText();
                log.info("订单号：{}，微信支付V3成功，交易ID：{}", orderNumber, transactionId);

                // TODO: 更新订单状态
                // updateOrderStatus(orderNumber, transactionId);
            }

        } catch (Exception e) {
            log.error("处理微信支付V3通知失败", e);
            throw new RuntimeException("处理支付通知失败", e);
        }
    }

    /**
     * 微信退款V3接口
     */
    @Override
    public boolean refund(String orderNumber, BigDecimal refundAmount, String refundReason) {
        log.info("开始处理微信支付V3退款，订单号：{}，退款金额：{}", orderNumber, refundAmount);

        try {
            Order order = orderMapper.getByNumber(orderNumber);
            if (order == null) {
                throw new RuntimeException("订单不存在，无法退款");
            }

            if (refundAmount.compareTo(order.getAmount()) > 0) {
                throw new IllegalArgumentException("退款金额不能超过订单金额");
            }

            // 构建退款请求体
            Map<String, Object> requestBody = new HashMap<>();
            requestBody.put("out_trade_no", orderNumber);
            requestBody.put("out_refund_no", "REFUND_" + orderNumber + "_" + System.currentTimeMillis());
            requestBody.put("reason", refundReason != null ? refundReason : "用户退款");

            // 金额信息
            Map<String, Object> amount = new HashMap<>();
            amount.put("refund", refundAmount.multiply(new BigDecimal("100")).intValue());
            amount.put("total", order.getAmount().multiply(new BigDecimal("100")).intValue());
            amount.put("currency", "CNY");
            requestBody.put("amount", amount);

            String jsonBody = objectMapper.writeValueAsString(requestBody);
            String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";

            String responseBody = sendV3Request("POST", url, jsonBody);
            JsonNode responseJson = objectMapper.readTree(responseBody);

            String status = responseJson.get("status").asText();
            boolean success = "SUCCESS".equals(status) || "PROCESSING".equals(status);

            if (success) {
                log.info("退款请求成功，订单号：{}，退款状态：{}", orderNumber, status);
                // 更新订单状态为已退款
                orderMapper.updateStatus(orderNumber, Order.REFUNDED, java.time.LocalDateTime.now());
            } else {
                log.error("退款失败，订单号：{}，响应：{}", orderNumber, responseBody);
            }

            return success;

        } catch (Exception e) {
            log.error("退款处理失败，订单号：{}", orderNumber, e);
            throw new RuntimeException("退款处理失败：" + e.getMessage(), e);
        }
    }

    /**
     * 发送微信支付V3请求
     */
    private String sendV3Request(String method, String url, String body) {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-Type", "application/json");
            httpPost.addHeader("Authorization", buildAuthorizationHeader(method, url, body));
            httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));

            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                int statusCode = response.getStatusLine().getStatusCode();
                String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
                
                log.info("微信支付V3接口返回，状态码：{}，响应：{}", statusCode, responseBody);
                
                if (statusCode >= 200 && statusCode < 300) {
                    return responseBody;
                } else {
                    throw new RuntimeException("微信支付V3接口调用失败，状态码：" + statusCode + "，响应：" + responseBody);
                }
            }
        } catch (Exception e) {
            log.error("发送微信支付V3请求失败", e);
            throw new RuntimeException("发送请求失败", e);
        }
    }

    /**
     * 构建微信支付V3授权头
     */
    private String buildAuthorizationHeader(String method, String url, String body) {
        try {
            long timestamp = Instant.now().getEpochSecond();
            String nonceStr = UUID.randomUUID().toString().replace("-", "");
            String canonicalUrl = url.substring(url.indexOf("/v3"));

            // 构建签名串
            String signStr = method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";

            // 生成签名
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(merchantPrivateKey);
            signature.update(signStr.getBytes(StandardCharsets.UTF_8));
            String sign = Base64.encodeBase64String(signature.sign());

            // 构建Authorization头
            String authHeader = String.format("WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"",
                    weChatPayConfig.getMchId(), nonceStr, timestamp, platformCertSerialNo, sign);
            log.info("构建的Authorization头：{}", authHeader);
            return authHeader;
        } catch (Exception e) {
            log.error("构建微信支付V3授权头失败", e);
            throw new RuntimeException("构建授权头失败", e);
        }
    }

    /**
     * 构建前端JSAPI调起支付参数
     */
    private PayVO buildJSAPIPayVO(String prepayId) {
        try {
            String appId = weChatPayConfig.getAppId();
            String timeStamp = String.valueOf(Instant.now().getEpochSecond());
            String nonceStr = UUID.randomUUID().toString().replace("-", "");
            String packageStr = "prepay_id=" + prepayId;

            // V3版本JSAPI签名
            String signStr = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n";
            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initSign(merchantPrivateKey);
            signature.update(signStr.getBytes(StandardCharsets.UTF_8));
            String paySign = Base64.encodeBase64String(signature.sign());

            return PayVO.builder()
                    .appId(appId)
                    .timeStamp(timeStamp)
                    .nonceStr(nonceStr)
                    .packageStr(packageStr)
                    .signType("RSA")  // V3使用RSA签名
                    .paySign(paySign)
                    .build();

        } catch (Exception e) {
            log.error("构建前端支付参数失败", e);
            throw new RuntimeException("构建支付参数失败", e);
        }
    }

    /**
     * 验证微信支付V3通知签名
     */
    private boolean verifyNotifySignature(String notifyData) {
        // TODO: 从请求头中获取签名信息进行验证
        // 这里需要从HttpServletRequest中获取Wechatpay-Signature等头信息
        // 由于当前方法签名限制，暂时返回true，实际应用中需要完整实现
        return true;
    }

    /**
     * 解密微信支付V3通知资源数据
     */
    private String decryptResource(String ciphertext, String associatedData, String nonce) {
        try {
            String apiKey = weChatPayConfig.getApiKey();
            SecretKeySpec key = new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
            
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, key, gcmParameterSpec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            
            byte[] decryptedBytes = cipher.doFinal(Base64.decodeBase64(ciphertext));
            return new String(decryptedBytes, StandardCharsets.UTF_8);
            
        } catch (Exception e) {
            log.error("解密微信支付V3通知数据失败", e);
            throw new RuntimeException("解密通知数据失败", e);
        }
    }
}